package naito_rescue.agent;

import java.util.*;

import rescuecore2.worldmodel.*;
import rescuecore2.standard.messages.*;
import rescuecore2.messages.*;
import rescuecore2.log.Logger;

import rescuecore2.standard.entities.*;

import naito_rescue.*;
import naito_rescue.agent.*;
import naito_rescue.object.*;
import naito_rescue.router.*;
import naito_rescue.task.*;
import naito_rescue.task.job.*;
import naito_rescue.message.*;
import naito_rescue.message.manager.*;

/**
*  救急隊だよ
*
*/
public class NAITOAmbulanceTeam extends NAITOHumanoidAgent<AmbulanceTeam>
{
	private Collection<StandardEntity> unexploredBuildings;
	private List<NAITOBuilding> myRescueAreas;
	private List<StandardEntity> rescueingMembers;
	private StandardEntity target_building;
	private int            team;
	private AmbulanceTeam  me;
	private boolean        isPreferredVoice;
	private boolean        isPreferredNear;
	private List<StandardEntity> refuges;
	private List<StandardEntity> targetsByMessage;
	private double rescueingWidth;
	private double rescueingHeight;
	private double rescueingStartX;
	private double rescueingStartY;
	
	
	protected void postConnect(){
       super.postConnect();
		model.indexClass(StandardEntityURN.CIVILIAN, StandardEntityURN.FIRE_BRIGADE, StandardEntityURN.POLICE_FORCE, StandardEntityURN.AMBULANCE_TEAM, StandardEntityURN.REFUGE, StandardEntityURN.BUILDING);
		unexploredBuildings = model.getEntitiesOfType(StandardEntityURN.BUILDING);
		
		refuges = new ArrayList<StandardEntity>();
		for(StandardEntity refuge : model.getEntitiesOfType(StandardEntityURN.REFUGE)){
			refuges.add(refuge);
		}
		this.targetsByMessage = new ArrayList<StandardEntity>();
		this.rescueingMembers = new ArrayList<StandardEntity>();
		this.rescueingMembers.addAll(this.atList);
		this.myRescueAreas = new ArrayList<NAITOBuilding>();
	}
	private void decideRescueingArea(){
		int    roleID = rescueingMembers.indexOf(me());
		int    oldRoleID = roleID;
		int    separateBlock = 1;
		
		
		//DisasterSpaceをいくつのブロックに分割するかを決める
		for(; (separateBlock * separateBlock) < rescueingMembers.size();separateBlock++);

		separateBlock--;
		if(separateBlock < 1) separateBlock = 1;

		//roleIDの正規化...roleID=[0 ... pow(separateBlock,2)-1]になるように
		while(roleID >= (separateBlock * separateBlock)) roleID -= separateBlock;
		
		String context = "decideRescueingArea";
		//logger.debug("rescueingMembers.size() = " + rescueingMembers.size(), context);
		//logger.debug("roleID = " + oldRoleID, context);
		//logger.debug("separateBlock = " + separateBlock, context);
		//logger.debug("Normalized roleID = " + roleID, context);
		//logger.debug("w_minX = " + w_minX, context);
		//logger.debug("w_minY = " + w_minY, context);
		//logger.debug("w_maxX = " + w_maxX, context);
		//logger.debug("w_maxY = " + w_maxY, context);
		//どっからどこまでのBuildingを探訪するか決定する
		rescueingWidth  = (w_maxX - w_minX) / separateBlock;
		rescueingHeight = (w_maxY - w_minY) / separateBlock;
		rescueingStartX      = w_minX + rescueingWidth * (roleID % separateBlock);
		rescueingStartY      = w_minY + rescueingHeight * (roleID / separateBlock);
		
		//logger.debug("rescueingWidth = " + rescueingWidth, context);
		//logger.debug("rescueingHeight = " + rescueingHeight, context);
		//logger.debug("rescueingStartX = " + rescueingStartX, context);
		//logger.debug("rescueingStartY = " + rescueingStartY, context);
		Building b = null;
		for(StandardEntity building : allBuildings){
			b = (Building)building;
			if(b.getX() > rescueingStartX && b.getX() <= (rescueingStartX + rescueingWidth) &&
			   b.getY() > rescueingStartY && b.getY() <= (rescueingStartY + rescueingHeight)){
				myRescueAreas.add(allNAITOBuildings.get(b.getID()));
				//logger.debug("clearAroundHeres.add(ID=" + b.getID() + ")", context);
			}
		}
	}
	public String toString(){
		return "NAITOAmbulanceTeam." + me().getID() + "";
	}

	
	protected void think(int time, ChangeSet changed, Collection<Command> heard){
		super.think(time, changed, heard);
		String context = "NAITOAmbulanceTeam#think";
		if (time < config.getIntValue(kernel.KernelConstants.IGNORE_AGENT_COMMANDS_KEY)){
			return;
		}
		if (time == config.getIntValue(kernel.KernelConstants.IGNORE_AGENT_COMMANDS_KEY)) {
			// Subscribe to channel 1
			/*
			if(!crowlingBuildings.isEmpty()){
				for(Building b : crowlingBuildings){
					List<EntityID> path1 = search.AStar4(getLocation().getID(), b.getID());
					List<EntityID> path2 = search.breadthFirstSearch(getLocation(), b);
				}
			}
			*/
			if(isNeedRescue()){
				//rescue(me().getID());
				return;
			}
		}
		//受信メッセージの取得
		List<NAITOMessage> received = commManager.getMessages(time);
		for(NAITOMessage message : received){
			if(message instanceof CivilianInBuildingMessage){
				// ターゲットとなる建物を追加(If new)
				List<EntityID> targetIDs = ((CivilianInBuildingMessage)message).getIDs();
				addTargetIfNew(targetIDs);
			}else if(message instanceof HelpMeInBuildingMessage){
				EntityID targetID = ((HelpMeInBuildingMessage)message).getBuildingID();
				addTargetIfNew(Arrays.asList(targetID));
			}
		}
		updateExploredTarget();
		// updateUnexploredBuildings(changed);
		// Am I transporting a civilian to a refuge?
		if (someoneOnBoard() != null) {
			// Am I at a refuge?
			if (location() instanceof Refuge) {
				// Unload!
				Logger.info("Unloading");
				sendUnload(time);
				return;
			}
			else {
				// Move to a refuge
				List<EntityID> path = search.breadthFirstSearch(refuges);
				if (path != null) {
					Logger.info("Moving to refuge");
					//logger.info("Someone on board, move to refuge.", context);
					sendMove(time, path);
					return;
				}
				// What do I do now? Might as well carry on and see if we can dig someone else out.
				Logger.debug("Failed to plan path to refuge");
			}
		}
		// Go through targets (sorted by distance) and check for things we can do
		for (Human next : getTargets()) {
			if (next.getPosition().equals(location().getID())) {
				// Targets in the same place might need rescueing or loading
				if ((next instanceof Civilian) && next.getBuriedness() == 0 && !(location() instanceof Refuge)) {
					// Load
					Logger.info("Loading " + next);
					sendLoad(time, next.getID());
					return;
				}
				if (next.getBuriedness() > 0) {
					// Rescue
					Logger.info("Rescueing " + next);
					sendRescue(time, next.getID());
					return;
				}
			}
			else {
				// Try to move to the target
				List<EntityID> path = search.breadthFirstSearch(getLocation(), next);
				if (path != null) {
					Logger.info("Moving to target");
					sendMove(time, path);
					return;
				}
			}
		}
		
		if(!targetsByMessage.isEmpty()){
			//logger.debug("!targetsByMessage.isEmpty", context);
			StandardEntity target = selectRescueTarget();
			//logger.debug("selectTarget = " + target, context);
			if(target != null){
				List<EntityID> pathToTargetBuildings = search.getRoute(target.getID());
				if(pathToTargetBuildings != null){
					//logger.info("Move to targets by messages. target = " + target, context);
					move(pathToTargetBuildings);
					return;
				}else{
					//logger.info("path is null", context);
				}
			}
		}
		//logger.info("doDefaultSearch()", context);
		doDefaultSearch();
	}
	private StandardEntity selectRescueTarget(){
		List<StandardEntity> targets = new ArrayList<StandardEntity>();
		boolean isIncludedMyArea = false;
		for(StandardEntity entity : targetsByMessage){
			Building b = (Building)entity;
			if(b.getX() > rescueingStartX && b.getX() <= (rescueingStartX + rescueingWidth) &&
			   b.getY() > rescueingStartY && b.getY() <= (rescueingStartY + rescueingHeight)){
				isIncludedMyArea = true;
				targets.add(entity);
				//logger.debug("clearAroundHeres.add(ID=" + b.getID() + ")", context);
			}
		}
		if(!isIncludedMyArea){
			targets.addAll(targetsByMessage);
		}
		for(Iterator<StandardEntity> iterator = targets.iterator();iterator.hasNext();){
			StandardEntity entity = iterator.next();
			List<EntityID> path = search.breadthFirstSearch(getLocation(), entity);
			if(path == null){
				iterator.remove();
			}
		}
		Collections.sort(targets, new DistanceSorter(getLocation(), this.model));
		//logger.debug("targets=" + targets, "selectRescueTarget");
		if(!targets.isEmpty()){
			return targets.get(0);
		}else{
			return null;
		}
	}
	private void addTargetIfNew(List<EntityID> IDs){
		for(EntityID id : IDs){
			StandardEntity building = model.getEntity(id);
			if(!targetsByMessage.contains(building)){
				targetsByMessage.add(building);
			}
		}
	}
	protected void updateExploredTarget(){
		if(!targetsByMessage.isEmpty()){
			Area location = (Area)(getLocation());
			for(Iterator<StandardEntity> iterator = targetsByMessage.iterator();iterator.hasNext();){
				StandardEntity entity = iterator.next();
				if(location.getID().getValue() == entity.getID().getValue()){
					//logger.debug("Remove area has visited to rescue.", "NAITOAmbulanceTeam#updateExploredTarget");
					iterator.remove();
				}
			}	
		}
	}
    protected EnumSet<StandardEntityURN> getRequestedEntityURNsEnum() {
		return EnumSet.of(StandardEntityURN.AMBULANCE_TEAM);
	}
    private List<Human> getTargets() {
        List<Human> targets = new ArrayList<Human>();
        for (StandardEntity next : model.getEntitiesOfType(StandardEntityURN.CIVILIAN, StandardEntityURN.FIRE_BRIGADE, StandardEntityURN.POLICE_FORCE, StandardEntityURN.AMBULANCE_TEAM)) {
            Human h = (Human)next;
            if (h == me()) {
                continue;
            }
            if (h.isHPDefined()
                && h.isBuriednessDefined()
                && h.isDamageDefined()
                && h.isPositionDefined()
                && h.getHP() > 0
                && (h.getBuriedness() > 0 || h.getDamage() > 0)
				&& h.getBuriedness() > 0){
                
                targets.add(h);
            }
        }
        Collections.sort(targets, new DistanceSorter(location(), model));
        return targets;
    }

}
